//@author A0083338B

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Scanner;
import java.util.TreeMap;

import jxl.write.WriteException;

public class Driver {

	// ATTRIBUTES

	// Logic layer
	private Account account;

	// Presentation layer
	private OperationHistory ops;
	private CommandHistory his;
	private AutoFill robot;
	private Print print;

	// Helper variables
	private Scanner scanner;
	private String title;
	private String[] commands;
	private List<Transaction> transactions;
	private boolean alert;

	// File/folder constants
	protected static final String ASSETS_FOLDER = "assets";
	protected static final String ASSETS_PATH = System.getProperty("user.dir") + "/" + ASSETS_FOLDER + "/";
	protected static final String HELP_FILE = ASSETS_PATH + "help.txt";
	protected static final String SPLASH_FILE = ASSETS_PATH + "splash.txt";

	// Command constants
	private static final int COMMAND_MAIN = 0;
	private static final int COMMAND_SECOND = 1;


	// Flag constants
	private static final int TYPE = 2;
	private static final int NAME = 3;
	private static final int VALUE = 4;
	private static final int DATE = 5;
	private static final int CATEGORY = 6;
	private static final int REMARKS = 7;
	private static final int STATUS = 8;
	private static final int ID = 9;
	private static final int PAYMENTMETHOD = 10;
	private static final int PRIORITY = 11;


	// CONSTRUCTOR
	private Driver() {
		this.scanner = new Scanner(System.in);
		this.account = new Account();
		this.his = new CommandHistory();
		this.transactions = account.getTransactions().getTransactions();
		this.robot = new AutoFill();
		this.ops = new OperationHistory(account);
		this.title= "";
		this.print = new Print();
		this.alert = false;
	}

	// METHODS

	// Getters
	public CommandHistory getInputHistory() {
		return this.his;
	}
	public List<Transaction> getTransactions() {
		return this.transactions;
	}
	public String getTitle(){
		return this.title;
	}

	// Setters
	public void setTitle(String title){
		this.title = title;
	}

	// Run
	private void run() {

		// Variables
		String cmd;
		boolean flag;
		String str = null;

		// Read command
		this.alert();
		this.print.separator();
		System.out.println();
		this.print.prompt("Enter command");
		cmd = this.scanner.nextLine();

		// Go back to previous/next input
		while (cmd.equalsIgnoreCase("p")) {
			try{
				this.getInputHistory().pop();			
				cmd = this.scanner.nextLine();
			} catch (Exception e) {
				this.print.feedback("No previous command available");
				return;
			}
		}
		while (cmd.equalsIgnoreCase("n")) {
			try{
				this.getInputHistory().dpop();
				cmd = this.scanner.nextLine();
			} catch (Exception e) {
				this.print.feedback("No next command available");
				return;
			}
		}
		his.addInput(cmd); // add command to stack
		commandAnalysis(cmd); // analyse command

		// start ADD
		if (commands[COMMAND_MAIN].equalsIgnoreCase("add") && commands[COMMAND_SECOND] != null) {
			if (commands[COMMAND_SECOND].equalsIgnoreCase("-c")) { // add category
				this.print.command("Add category");
				if (commands[CATEGORY] == null) {
					this.print.prompt("Enter category name");
					str = this.scanner.next();
					scanner.nextLine(); // move to next line
				} else {
					str = commands[CATEGORY];
				}
				if (account.addCategory(str)) {
					this.print.feedback("Added category");
					// undo/redo
					ops.add(new Operation("add -c", str, false, true));
				} else {
					this.print.feedback("Category already exists");
				}
			}else if (commands[COMMAND_SECOND].equalsIgnoreCase("-m")) { // add payment method
				this.print.command("Add payment method");
				if (commands[PAYMENTMETHOD] == null) {
					this.print.prompt("Enter payment method name");
					str = this.scanner.next();
					scanner.nextLine(); // move to next line
				} else {
					str = commands[PAYMENTMETHOD];
				}
				if (account.addPaymentMethod(str)) {
					this.print.feedback("Added payment method");
					// undo/redo
					ops.add(new Operation("add -m", str, false, true));
				} else {
					this.print.feedback("Payment method already exists");
				}

			}else { // add transaction or budget planner item
				Transaction transaction;
				if (commands[COMMAND_SECOND].equalsIgnoreCase("-b")) { // add budget planner item
					this.print.command("Add budget planner item");
					flag = true;
				} else if(commands[COMMAND_SECOND].equalsIgnoreCase("-t")) { // add transaction
					this.print.command("Add transaction");
					flag = false;
				} else {
					this.print.feedback("Invalid command");
					return;
				}
				try {
					transaction = readTransaction(); // read in the transaction or budget planner item
				} catch (Exception e) {
					this.print.feedback("Invalid command");
					return;
				}
				if (transaction == null) {
					this.print.feedback("Invalid command");
				}
				if (flag) {
					try{
						account.getBudgetPlanner().addItem((BudgetPlannerItem)transaction);		
						this.print.feedback("Budget planner item added");
					} catch (Exception e) {
						return;
					}
				} else {
					try{
						account.addTransaction(transaction);
						this.print.feedback("Transaction added");
					} catch (Exception e) {
						return;
					}
				}
				// undo/redo
				ops.add(new Operation(commands[COMMAND_MAIN], transaction, false, false));
			}
		} // end ADD

		// start UPDATE
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("update") && commands[COMMAND_SECOND] != null) {
			if (commands[COMMAND_SECOND].equalsIgnoreCase("-c")) { // update category
				String str2;
				this.print.command("Update category");
				if (commands[CATEGORY] == null) {
					this.print.prompt("Enter category name");
					str = this.scanner.next();
					scanner.nextLine(); // move to next line
				} else {
					str = commands[CATEGORY];
				}
				this.print.prompt("Enter new category name");
				str2 = this.scanner.next();

				if (account.updateCategory(str, str2)) {
					this.print.feedback("Category updated");
					// undo/redo
					ops.add(new Operation("-c", str, true, true));
					ops.add(new Operation("-c", str2, true, true));
				} else {
					this.print.feedback("Category does not exist");
				}
				scanner.nextLine(); // move to next line
			} else if (commands[COMMAND_SECOND].equalsIgnoreCase("-m")) { // update payment method
				String str2;
				this.print.command("Update payment method");
				if (commands[PAYMENTMETHOD] == null) {
					this.print.prompt("Enter payment method name");
					str = this.scanner.next();
					scanner.nextLine(); // move to next line
				} else {
					str = commands[PAYMENTMETHOD];
				}
				this.print.prompt("Enter new payment method name");
				str2 = this.scanner.next();

				if (account.updatePaymentMethod(str, str2)) {
					this.print.feedback("Payment method updated");
					// undo/redo
					ops.add(new Operation("-m", str, true, true));
					ops.add(new Operation("-m", str2, true, true));
				} else {
					this.print.feedback("Payment method does not exist");
				}
				scanner.nextLine(); // move to next line
			} else { // update transaction or budget planner item
				int num;			
				if (commands[COMMAND_SECOND].equalsIgnoreCase("-b")) {
					this.print.command("Update budget planner item");
					flag = true;
				} else if (commands[COMMAND_SECOND].equalsIgnoreCase("-t")) {
					this.print.command("Update transaction");
					flag = false;
				} else {
					this.print.feedback("Invalid command");
					return;
				}

				try{
					if (commands[ID] != null) {
						num = Integer.parseInt(commands[ID]);
					}  else {
						if(!flag) this.print.prompt("Enter ID of transaction");
						else this.print.prompt("Enter ID of BudgetPlanner Item");
						num = this.scanner.nextInt();
						scanner.nextLine();
					}
				} catch (Exception e) {
					this.print.feedback("Invalid command");					
					return;
				}

				Transaction tmp1;
				Transaction tmp2;
				try {
					if(!flag) {
						tmp1 = setter(account.getTransaction(num).clone());
					} else {
						tmp1 = setter(account.getBudgetPlanner().getItem(num).clone());
					}
				} catch (NullPointerException e) {
					this.print.feedback("ID does not exist");
					return;
				} catch (Exception e) {
					this.print.feedback("Invalid Command");
					return;
				}
				if(tmp1 != null) {
					if(flag) {
						tmp2 = account.getBudgetPlanner().updateItem(num, (BudgetPlannerItem)tmp1);
					} else {
						tmp2 = account.updateTransaction(num, tmp1);
					}
				}
				else {
					this.print.prompt("Enter \"y\" to update transaction #" + num + ", or \"n\" to cancel");
					if(!scanner.nextLine().equalsIgnoreCase("y")) return;
					try {
						if(!flag){
							robot.input("update -t " + AutoFill.readTransaction(account.getTransaction(num)));
						} else {
							robot.input("update -b " + AutoFill.readTransaction(account.getBudgetPlanner().getTransaction(num)));
						}
					}catch(NullPointerException e) {
						print.feedback("ID does not exist");
						return;
					} catch (Exception e) {
						this.print.feedback("Invalid Command");
						return;
					}
					String buffx = this.scanner.nextLine();
					commandAnalysis(buffx);
					try{
						tmp1 = readTransaction();
					} catch (Exception e) {
						this.print.feedback("Invalid command");
						return;
					}
					if(tmp1 == null) {
						this.print.feedback("Invalid command");
						return;
					}
					if(flag) {
						tmp2 = account.getBudgetPlanner().updateItem(num, (BudgetPlannerItem)tmp1);
					} else {
						tmp2 = account.updateTransaction(num, tmp1);
					}
				}
				// undo/redo
				ops.add(new Operation(num + "", tmp2, true, false));
				ops.add(new Operation(num + "", tmp1, true, false));
				if (!flag) {
					this.print.feedback("Transaction #" + num + " updated");
				} else {
					this.print.feedback("Budget planner item #" + num + " updated");
				}
			}

		} // end UPDATE

		// start DELETE
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("delete") && commands[COMMAND_SECOND] != null) {
			try {
				if(commands[COMMAND_SECOND].equalsIgnoreCase("-c")){  //delete category
					this.print.command("Delete category");
					if(commands[CATEGORY] == null ) {
						this.print.prompt("Enter category name");
						str = this.scanner.next(); 
						if(str == null) {
							print.feedback("Invalid command");
							return;
						}
						commands[CATEGORY] = str;
						scanner.nextLine(); // move to next line
					}
					this.print.prompt("Enter \"y\" to delete, or \"n\" to cancel");
					str = this.scanner.nextLine();
					if (str.equalsIgnoreCase("y")) {
						// Undo/redo
						if(commands[CATEGORY].equalsIgnoreCase(Account.DEFAULT_CATEGORY)) {
							this.print.feedback("You cannot delete the " + Account.DEFAULT_CATEGORY + " category");
							return;
						}
						if (account.deleteCategory(commands[CATEGORY])) {
							ops.add(new Operation("delete -c", commands[CATEGORY], false, true));
							this.print.feedback("Category deleted");
						} else {
							this.print.feedback("Category does not exist");
						}

					} else {
						this.print.feedback("Cancelled");
					}
					return;
				}
			} catch(Exception e) {e.printStackTrace(); return;}
			try {
				if(commands[COMMAND_SECOND].equalsIgnoreCase("-m")){  //delete payment method
					this.print.command("Delete payment method");
					if(commands[PAYMENTMETHOD] == null ) {
						this.print.prompt("Enter payment method name");
						str = this.scanner.next();
						if(str == null) {
							print.feedback("Invalid command");
							return;
						}
						commands[PAYMENTMETHOD] = str;
						scanner.nextLine(); // move to next line
					}
					this.print.prompt("Enter \"y\" to delete, or \"n\" to cancel");
					str = this.scanner.nextLine();
					if (str.equalsIgnoreCase("y")) {
						// Undo/redo
						if(commands[PAYMENTMETHOD].equalsIgnoreCase(Account.DEFAULT_PAYMENT_METHOD)) {
							this.print.feedback("You cannot delete the " + Account.DEFAULT_PAYMENT_METHOD + " payment method");
							return;
						}

						if (account.deletePaymentMethod(commands[PAYMENTMETHOD])) {
							ops.add(new Operation("delete -m", commands[PAYMENTMETHOD], false, true));
							this.print.feedback("Payment method deleted");
						} else {
							this.print.feedback("Payment method does not exist");
						}
					} else {
						this.print.feedback("Cancelled");
					}
					return;
				}
			} catch(Exception e) {e.printStackTrace(); return;}

			boolean valid = true;
			if (commands[COMMAND_SECOND].equalsIgnoreCase("-b")) {
				flag = true;
			} else if(commands[COMMAND_SECOND].equalsIgnoreCase("-t")) {
				flag = false;
			} else {
				print.feedback("Invalid command");
				return;
			}
			if (flag) {
				this.print.command("Delete budget planner item");
			} else {
				this.print.command("Delete transaction");
			}
			int num = 0;
			if(commands[ID] == null) {
				do {
					try{
						if (flag) {
							this.print.prompt("Enter budget planner item ID");
						} else {
							this.print.prompt("Enter transaction ID");
						}
						num = this.scanner.nextInt();
						scanner.nextLine(); // move to next line
						valid = true;
					} catch (Exception e) {
						this.print.feedback("Invalid command");
						valid = false;
					}
				}while((valid == false));
			}
			else { 
				try{
					num = Integer.parseInt(commands[9]);
				} catch (Exception e) {
					this.print.feedback("Invalid command");
					return;
				}
			}
			do{
				this.print.prompt("Enter \"y\" to delete, or \"n\" to cancel");
				str = this.scanner.next();
				Transaction buff;
				try {
					if(str.equalsIgnoreCase("y")) {
						if(!flag) buff = account.deleteTransaction(num);
						else buff = account.getBudgetPlanner().deleteTransaction(num);
						//redo/undo
						Operation t = new Operation(commands[COMMAND_MAIN], buff, false, false);
						ops.add(t);
						//
						if (flag) {
							this.print.feedback("Budget planner item deleted");
						} else {
							this.print.feedback("Transaction deleted");
						}
					}else {
						this.print.feedback("Cancelled");
					}
					valid = true;
				} catch(Exception e) {
					this.print.feedback("ID does not exist");
					valid = false;
				}
				scanner.nextLine(); // move to next line
			}while(valid == false);
		} // end DELETE

		// start LIST
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("list") && commands[COMMAND_SECOND] != null) {
			if (commands[COMMAND_SECOND].equalsIgnoreCase("-c")) { // list all categories
				this.print.command("List all categories");
				printCategories(account.getCategoriesList());
				return;
			}
			if (commands[COMMAND_SECOND].equalsIgnoreCase("-m")) { // list all categories
				this.print.command("List all payment methods");
				printPaymentMethod(account.getPaymentMethodsList());
				return;
			}
			this.setTitle("");
			if (commands[COMMAND_SECOND].equalsIgnoreCase("-b")) { // list all budget planner items
				this.print.command("List all budget planner items");
				this.transactions = account.getBudgetPlanner().getTransactions();
				this.printBudgetPlanner();
			} 
			else if (commands[COMMAND_SECOND].equalsIgnoreCase("-t")) { // list all transactions
				this.print.command("List all transactions");
				this.transactions = account.getTransactions().getTransactions();
				this.printTransactions();
			}
			else {
				this.print.feedback("Invalid command");
				return;
			}
		} // end LIST

		// start REPORT
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("report")) {
			if (commands[COMMAND_SECOND] == null) {
				this.print.prompt("Enter a date");
				commands[COMMAND_SECOND] = scanner.nextLine();
			}
			try{
				this.printReport(commands[COMMAND_SECOND], account.report(commands[COMMAND_SECOND]));
			} catch (Exception e) {
				this.print.feedback("Invalid command");
				return;
			}
		}// end REPORT

		// start FILTER
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("filter") && commands[COMMAND_SECOND] != null) {
			try {
				if (commands[DATE] != null) {
					this.print.command("Filter transactions by date");
					transactions = account.filterByDate(commands[DATE]);
					this.setTitle(commands[DATE]);
				}
				else if (commands[CATEGORY] != null) {
					this.print.command("Filter transactions by category");
					transactions = account.filterByCategory(commands[CATEGORY]);
					this.setTitle(commands[CATEGORY]);
				}
				else if (commands[PAYMENTMETHOD] != null) {
					this.print.command("Filter transactions by payment method");
					transactions = account.filterByPaymentMethod(commands[PAYMENTMETHOD]);
					this.setTitle(commands[PAYMENTMETHOD]);
				}
				else if (commands[STATUS] != null) {
					this.print.command("Filter transactions by status");
					transactions = account.filterByStatus(commands[STATUS]);
					this.setTitle(commands[STATUS]);
				}
				else {
					this.print.feedback("Please specify a parameter after the command");
					return;
				}
				this.printTransactions();
			} catch (Exception e) {
				this.print.feedback("Invalid command");
				return;
			}
		} // end FILTER

		// start SORT
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("sort")&& commands[COMMAND_SECOND] != null) {
			try{
				if(commands[COMMAND_SECOND].equalsIgnoreCase("-n")) {
					this.print.command("Sort by name");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else if(commands[COMMAND_SECOND].equalsIgnoreCase("-s")) {
					this.print.command("Sort by status");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else if(commands[COMMAND_SECOND].equalsIgnoreCase("-c")) {
					this.print.command("Sort by category");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else if(commands[COMMAND_SECOND].equalsIgnoreCase("-v")) {
					this.print.command("Sort by value");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else if(commands[COMMAND_SECOND].equalsIgnoreCase("-r")) {
					this.print.command("Sort by remarks");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else if(commands[COMMAND_SECOND].equalsIgnoreCase("-m")) {
					this.print.command("Sort by payment method");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else if(commands[COMMAND_SECOND].equalsIgnoreCase("-d")) {
					this.print.command("Sort by date");
					transactions = Account.sort(transactions, commands[COMMAND_SECOND]);
				}
				else {
					this.print.feedback("Invalid command");
					return;
				}
				this.printTransactions();
			} catch (Exception e) {
				this.print.feedback("Invalid command");
				return;
			}
		} // end SORT

		// start UNDO
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("undo") && commands[COMMAND_SECOND] == null) {
			this.print.command("Undo");
			if(!ops.undo()) this.print.feedback("Cannot undo anymore");
		} // end UNDO

		// start REDO
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("redo") && commands[COMMAND_SECOND] == null) {
			this.print.command("Redo");
			if(!ops.redo()) this.print.feedback("Cannot redo anymore");
		} // end REDO

		// start SEARCH
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("search")) {
			this.print.command("SEARCH");
			Double value;
			if(commands[COMMAND_SECOND] == null) {
				print.feedback("Please enter a search term");
				commands[COMMAND_SECOND] = scanner.nextLine();
			}
			try {
				value = Double.parseDouble(commands[COMMAND_SECOND]);
				this.transactions = account.search(value);
			} catch(Exception e) {
				this.transactions = account.search(commands[COMMAND_SECOND]);
			}
			this.setTitle("\"" + commands[COMMAND_SECOND] + "\"");
			this.printTransactions();
		} // end SEARCH

		// start PLAN
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("plan")) {
			this.print.command("Budget Planner");
			account.getBudgetPlanner().clear();

			if(commands[COMMAND_SECOND] != null) {
				try {
					account.getBudgetPlanner().setBudget(Double.parseDouble(commands[COMMAND_SECOND]));
					this.print.feedback("Budget set to "+Double.parseDouble(commands[COMMAND_SECOND]));

				} catch (Exception e) {
					this.print.feedback("Invalid command");
					return;
				}
			} else {
				this.print.prompt("Enter your budget");
				try {
					double budget = scanner.nextDouble();
					account.getBudgetPlanner().setBudget(budget);
					this.print.feedback("Budget set to "+budget);
					scanner.nextLine();
				} catch (Exception e) {
					this.print.feedback("Invalid command");
					return;
				}
			}
		} // End PLAN

		// start BUDGET 
		else if(commands[COMMAND_MAIN].equalsIgnoreCase("budget")) {
			this.print.command("Set budget");

			if(commands[COMMAND_SECOND] != null) {
				try {
					account.getBudgetPlanner().setBudget(Double.parseDouble(commands[COMMAND_SECOND]));
					this.print.feedback("Budget set to"+Double.parseDouble(commands[COMMAND_SECOND]));

				} catch (Exception e) {
					this.print.feedback("Invalid command");
					return;
				}
			} else {
				this.print.prompt("Enter your budget");
				try {
					double budget = scanner.nextDouble();
					account.getBudgetPlanner().setBudget(budget);
					this.print.feedback("Budget set to"+budget);
					scanner.nextLine();
				} catch (Exception e) {
					this.print.feedback("Invalid command");
					return;
				}	
			}
		} // end BUDGET

		// start DECIDE
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("decide") && commands[COMMAND_SECOND] == null) {
			this.print.command("Decide");
			if (account.getBudgetPlanner().size() == 0) {
				this.print.feedback("No items inside Budget Planner; please add in items");
				return;
			}
			this.transactions = account.getBudgetPlanner().decide();
			this.printBudgetPlanner();
			this.print.separator();
			if (this.transactions.size() == 0) {
				this.print.feedback("Insufficient budget; unable to purchase any items");
				return;
			}
			this.print.prompt("Enter \"y\" to add these items to your account, or \"n\" to cancel");
			String answer = this.scanner.next();
			if(answer.equalsIgnoreCase("y")){
				for(int i=0;i<this.transactions.size();i++)
					account.addTransaction(this.transactions.get(i));
				account.getBudgetPlanner().added();
				this.print.feedback("Transactions added to account");
			} else{
				account.getBudgetPlanner().notAdded();
				this.print.feedback("Cancelled");
			}
			scanner.nextLine();
		} // end DECIDE

		// start IMPORT
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("import")) {
			this.print.command("Import");
			if(commands[COMMAND_SECOND] != null) str = commands[COMMAND_SECOND];
			else {
				this.print.prompt("Enter the filename of the spreadsheet to import");
				str = this.scanner.next();
				scanner.nextLine(); // move to next line
			}
			List<Transaction> newTransactions = null;
			try {
				newTransactions = importFile(str);
			} catch (Exception e) {
				this.print.feedback("Invalid directory or name");
				return;
			}
			for (int i=0; i<newTransactions.size(); i++) {
				account.addTransaction(newTransactions.get(i));
			}
			this.print.feedback("Transactions imported");
		} // end IMPORT

		// start EXPORT
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("export")) {
			this.print.command("Export");
			if(commands[COMMAND_SECOND] != null) str = commands[COMMAND_SECOND];
			else {
				this.print.prompt("Enter filename for the Excel spreadsheet");
				str = this.scanner.nextLine();
			}
			try {
				exportFile(account.getTransactions(), str);
			} catch (WriteException e) {
				this.print.feedback("Invalid directory or name");
				return;
			} catch (IOException e) {
				this.print.feedback("Invalid directory or name");
				return;
			}
			this.print.feedback("Spreadsheet exported");
		} // end EXPORT


		// start CURR
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("curr")) {
			this.print.command("Set currency");
			if(commands[COMMAND_SECOND] != null) str = commands[COMMAND_SECOND];
			else {
				File file = new File(Transactions.EXCHANGE_RATE_PATH);
				try {
					Scanner sc = new Scanner(file);
					this.print.blankLine();
					while (sc.hasNext()) {
						String curr = sc.next();
						this.print.centreAlign(curr);
						sc.next();
					}
					this.print.blankLine();
					this.print.separator();
					sc.close();
					this.print.prompt("Enter a currency");
					str = this.scanner.nextLine();

				} catch (FileNotFoundException e) {
					this.print.feedback("Please place " + Transactions.EXCHANGE_RATE_FILENAME + " file in the Simplex asset folder");
				}					
			}
			try {
				account.setCurrency(str);
			} catch (Exception e) {
				this.print.feedback("The Currency is unavailable");
				File file = new File(Transactions.EXCHANGE_RATE_PATH);
				try {
					Scanner sc = new Scanner(file);
					this.print.feedback("These are the available Currencies:");
					while (sc.hasNext()) {
						String curr = sc.next();
						System.out.print(curr+"  ");
						curr = sc.next();
					}
					sc.close();
					System.out.println();
				} catch (FileNotFoundException ex) {
					this.print.feedback("Please place " + Transactions.EXCHANGE_RATE_FILENAME + " file in the Simplex asset folder");
				}	
			}
			this.print.feedback("Currency set to " + account.getCurrency());
		} // end CURR

		// start HELP
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("help") && commands[COMMAND_SECOND] == null) {
			this.printHelp();
		} // end HELP

		// start QUIT
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("quit") && commands[COMMAND_SECOND] == null) {
			this.print.command("Quit");
			this.print.prompt("Enter \"y\" to quit, or \"n\" to cancel");
			str = this.scanner.next();
			if (str.equalsIgnoreCase("y")) {
				this.print.feedback("Goodbye");
				System.exit(0);
			} else {
				this.print.feedback("Cancelled");
			}
			scanner.nextLine(); // move to next line
		} // end QUIT

		// start RESET
		else if (commands[COMMAND_MAIN].equalsIgnoreCase("reset")) {
			this.print.command("Reset");
			transactions.clear();
			account.reset();
			this.print.feedback("Reset");
		} // end RESET

		// start DEMO-T
		else if (commands[COMMAND_MAIN].equals("demo-t")) {
			try {
				Transaction[] transactions = new Transaction[]{
						new Transaction("OS textbook", 20, "EXPENSE", "04.04.2013", "SCHOOL", "CASH", "CS2106", "PAID"),
						new Transaction("Library fine", 20, "EXPENSE", "04.04.2013", "SCHOOL", "CASH", "OS textbook", "OUTSTANDING"),
						new Transaction("Cab fare", 20, "EXPENSE", "04.04.2013", "TRANSPORT", "CASH", "Late", "PAID"),
						new Transaction("Cab fare", 20, "EXPENSE", "03.04.2013", "TRANSPORT", "CASH", "To school", "PAID"),
						new Transaction("EZ-Link", 50, "EXPENSE", "02.04.2013", "TRANSPORT", "CASH", "Nil", "PAID"),
						new Transaction("Desk lamp", 30, "EXPENSE", "02.04.2013", "HOUSEHOLD", "CREDIT CARD", "Nil", "OUTSTANDING"),
						new Transaction("Allowance", 600, "DEPOSIT", "01.04.2013", "HOUSEHOLD", "FUND TRANSFER", "Nil", "PAID"),
						new Transaction("Cab fare", 20, "EXPENSE", "26.03.2013", "TRANSPORT", "CASH", "Late", "PAID"),
						new Transaction("PSP", 400, "EXPENSE", "20.03.2013", "LEISURE", "CREDIT CARD", "Nil", "OUTSTANDING"),
						new Transaction("Cab fare", 18, "EXPENSE", "18.03.2013", "TRANSPORT", "CASH", "Late", "PAID"),
						new Transaction("XBox", 500, "EXPENSE", "14.03.2013", "LEISURE", "CREDIT CARD", "Nil", "OUTSTANDING"),
						new Transaction("Laptop", 1500, "EXPENSE", "14.03.2013", "SCHOOL", "CREDIT CARD", "Nil", "OUTSTANDING"),
						new Transaction("Cab fare", 20, "EXPENSE", "08.03.2013", "TRANSPORT", "CASH", "To school", "PAID"),
						new Transaction("CS textbook", 30, "EXPENSE", "05.03.2013", "SCHOOL", "CASH", "CS2103T", "PAID"),
						new Transaction("CS textbook", 30, "EXPENSE", "04.03.2013", "SCHOOL", "CASH", "CS2103", "PAID"),
						new Transaction("Cab fare", 18, "EXPENSE", "04.03.2013", "TRANSPORT", "CASH", "Late", "PAID"),
						new Transaction("School fee", 3000, "EXPENSE", "03.03.2013", "SCHOOL", "CHEQUE", "Nil", "OUTSTANDING"),
						new Transaction("EZ-Link", 50, "EXPENSE", "02.03.2013", "TRANSPORT", "CASH", "Nil", "PAID"),
						new Transaction("Allowance", 3600, "DEPOSIT", "01.03.2013", "HOUSEHOLD", "FUND TRANSFER", "Nil", "PAID"),
				};
				for (Transaction t: transactions) {
					this.account.addTransaction(t);
				}
				this.print.separator();
				this.print.feedback("19 transactions added");
			} catch (Exception e) {
				e.printStackTrace();
			}
		} // end DEMO-T

		// start DEMO-B
		else if (commands[COMMAND_MAIN].equals("demo-b")) {
			try {
				BudgetPlannerItem[] BPList = new BudgetPlannerItem[]{
						new BudgetPlannerItem("XBox controller",40 , "EXPENSE", "05.04.2013", "LEISURE", "CASH", "Nil", "OUTSTANDING", 40),
						new BudgetPlannerItem("Football jersey",115 , "EXPENSE", "05.04.2013", "LEISURE", "UNCATEGORISED", "Nil", "OUTSTANDING", 70),
						new BudgetPlannerItem("iPhone case",20 , "EXPENSE", "05.04.2013", "LEISURE", "UNCATEGORISED", "Nil", "OUTSTANDING", 10),
						new BudgetPlannerItem("School bag",75 , "EXPENSE", "05.04.2013", "SCHOOL", "CASH", "Nil", "OUTSTANDING", 60),
						new BudgetPlannerItem("Physics textbook",40 , "EXPENSE", "05.04.2013", "SCHOOL", "CASH", "PC1431", "OUTSTANDING", 100),
						new BudgetPlannerItem("Tumbler", 20, "EXPENSE", "05.04.2013", "HOUSEHOLD", "UNCATEGORISED", "Nil", "OUTSTANDING", 75),
						new BudgetPlannerItem("Calculator", 110, "EXPENSE", "05.04.2013", "SCHOOL", "CASH", "Nil", "OUTSTANDING", 50),
						new BudgetPlannerItem("Couch", 30, "EXPENSE", "05.04.2013", "HOUSEHOLD", "CASH", "Nil", "OUTSTANDING", 80),
						new BudgetPlannerItem("EZlink", 30, "EXPENSE", "05.04.2013", "TRANSPORT", "CASH", "Nil", "OUTSTANDING", 65),
				};
				for (BudgetPlannerItem t: BPList) {
					this.account.getBudgetPlanner().addItem(t);
				}
				this.print.separator();
				this.print.feedback("9 budget planner items added");
			} catch (Exception e) {
				e.printStackTrace();
			}
		} // end DEMO-B

		// start INVALID COMMAND
		else {
			this.print.feedback("Invalid command. Type \"help\" for the list of available commands");
		} // end INVALID COMMAND

		account.save();
	}

	// Parse commands, convert into a standard format
	private String[] commandAnalysis(String str) {
		this.commands = new String[13];
		String[] cmd = str.split(" ");
		int i = 0;
		if(cmd.length == 1) {
			commands[COMMAND_MAIN] = cmd[0].toLowerCase();
			return cmd;
		}
		commands[COMMAND_MAIN] = cmd[0].toLowerCase();
		commands[COMMAND_SECOND] = cmd[1].toLowerCase();
		if(cmd.length == 2) return commands;
		else i = 1;
		for(; i < cmd.length - 1; i++) {
			String a = cmd[i];
			if(a.equals("-n") && cmd[i + 1] != null) commands[NAME] = this.StringAnalysis(cmd, i);
			if(a.equals("-v") && cmd[i + 1] != null) commands[VALUE] = this.StringAnalysis(cmd, i);
			if(a.equals("-d") && cmd[i + 1] != null) commands[DATE] = this.StringAnalysis(cmd, i);
			if(a.equals("-c") && cmd[i + 1] != null)  commands[CATEGORY] = this.StringAnalysis(cmd, i);
			if(a.equals("-r") && cmd[i + 1] != null) commands[REMARKS] = this.StringAnalysis(cmd, i);
			if(a.equals("-s") && cmd[i + 1] != null) commands[STATUS] = this.StringAnalysis(cmd, i);
			if(a.equals("-id") && cmd[i + 1] != null) commands[ID] = this.StringAnalysis(cmd, i);
			if(a.equals("-m") && cmd[i + 1] != null) commands[PAYMENTMETHOD] = this.StringAnalysis(cmd, i);
			if(a.equals("-p") && cmd[i + 1] != null) commands[PRIORITY] = this.StringAnalysis(cmd, i);
		}
		if(commands[VALUE] != null) {
			if(commands[VALUE].charAt(0) == '+') {
				commands[TYPE] = "DEPOSIT";
			}
			else commands[TYPE] = "EXPENSE";
			if(commands[VALUE].charAt(0) == '-'|| commands[VALUE].charAt(0) == '+') commands[VALUE] = commands[VALUE].substring(1);
		}
		return commands;
	}
	// Helper method for command analysis
	private String StringAnalysis(String[] cmd, int i) {
		StringBuffer sb =new StringBuffer();
		int j = i + 1;
		for(int x = j; x < cmd.length; x++) {
			if(cmd[x].startsWith("-") && (cmd[x].length() == 2 || cmd[x].length() == 3)) break;
			sb.append(cmd[x] + " ");
		}
		if(sb.length() == 0) return null;
		sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}

	// Read in a Transaction object
	private Transaction readTransaction() throws Exception {
		Calendar cal = new GregorianCalendar();
		String today = cal.get(Calendar.DAY_OF_MONTH) + "." + (cal.get(Calendar.MONTH) + 1) + "." + cal.get(Calendar.YEAR);
		String type = commands[TYPE], name, date, category, remarks, status, paymentMethod;
		int priority = 0;
		double value = 0;
		if (type == null) {
			type = "EXPENSE";
		} 
		name = commands[NAME];
		if (name == null) {
			this.print.prompt("Transaction name");
			name = this.scanner.nextLine();
		}
		boolean valid = true;
		do {
			try {
				value = Double.parseDouble(commands[VALUE]);
				valid = true;
			}catch(Exception e) {
				this.print.prompt("Transaction value");
				commands[4] = this.scanner.nextLine();
				if(commands[4].charAt(0) == '+') {
					commands[4] = commands[4].substring(1);
					type = "DEPOSIT";
				}
				valid = false;
			}
		} while(valid == false);

		date = commands[DATE];
		if(date == null) date = today;
		category = commands[CATEGORY];
		if(category == null) category = Account.DEFAULT_CATEGORY;
		remarks = commands[REMARKS];
		if (remarks == null) remarks = Account.DEFAULT_REMARKS;
		status = commands[STATUS];
		if(status == null) status = Account.DEFAULT_STATUS;
		else { 
			while(status != null && !status.equalsIgnoreCase("OUTSTANDING") && !status.equalsIgnoreCase("PAID")) {
				this.print.prompt("Transaction status can only be PAID or OUTSTANDING");
				status = this.scanner.next();
				scanner.nextLine();
			}
		}
		paymentMethod = commands[PAYMENTMETHOD];
		if(paymentMethod == null) paymentMethod = Account.DEFAULT_PAYMENT_METHOD;
		if(commands[COMMAND_SECOND].equals("-b")) {
			do{
				try{
					priority = Integer.parseInt(commands[PRIORITY]);
					if (priority<1||priority>100) {
						this.print.feedback("Priority value should be from 1 to 100");
						valid =false;
						this.print.prompt("Priority");
						commands[11] = this.scanner.nextLine();
						
					} else valid = true;
				}catch(Exception e) {
					this.print.prompt("Priority value");
					commands[11] = this.scanner.nextLine();
					valid = false;
				}
			}while(valid == false);
		}
		try{
			if (!commands[COMMAND_SECOND].equals("-b")) {
				return new Transaction(name, value, type, date, category, paymentMethod, remarks, status);
			} else {
				return new BudgetPlannerItem(name, value, "EXPENSE", date, category, paymentMethod, remarks, "OUTSTANDING", priority);
			}
		}catch(Exception e) {
			return null;
		}
	}
	//transaction setters
	private Transaction setter(Transaction copy) {
		boolean count = false;

		if(commands[NAME] != null)  {
			count = true;
			copy.setName(commands[NAME]);
		}
		if(commands[VALUE] != null)  {
			count = true;
			copy.setValue(Double.parseDouble(commands[VALUE]));
		}
		if(commands[STATUS] != null)  {
			count = true;
			copy.setStatus(commands[STATUS]);
		}if(commands[CATEGORY] != null)  {
			count = true;
			copy.setCategory(commands[CATEGORY]);
		}if(commands[PAYMENTMETHOD] != null)  {
			count = true;
			copy.setPaymentMethod(commands[PAYMENTMETHOD]);
		}if(commands[REMARKS] != null)  {
			count = true;
			copy.setRemarks(commands[REMARKS]);
		}if(commands[TYPE] != null)  {
			count = true;
			copy.setType(commands[TYPE]);
		}if(commands[DATE] != null)  {
			count = true;
			copy.setDate(commands[DATE]);
		}if(commands[DATE] != null)  {
			count = true;
			copy.setDate(commands[DATE]);
		}
		BudgetPlannerItem t = null;
		if(copy instanceof BudgetPlannerItem) t =  (BudgetPlannerItem) copy;
		if(commands[PRIORITY] != null) {
			count = true;
			t.setPriority(Integer.parseInt(commands[PRIORITY]));
			return t;
		}
		if(!count) return null;
		return copy;
	}

	//@author A0085897A
	// Print transactions
	private void printTransactions() {
		double value;
		double[] stats;
		String[] cells;
		int[] cellWidths;
		Transaction transaction;
		if (!this.getTitle().equals("")) {
			this.print.blankLine();
			this.print.centreAlign(this.print.spaceOut(this.getTitle().toUpperCase()));
			this.print.separator('=');
		}
		cellWidths = new int[]{5, 13, 12, 13, 15, 15, 13, 12};
		cells = new String[]{"ID", "NAME", "VALUE", "DATE", "CATEGORY", "METHOD", "REMARKS", "STATUS"};
		this.print.blankLine();
		this.print.row(cells, cellWidths);
		this.print.separator('-');
		for (int i=0; i<this.getTransactions().size(); i++) {
			transaction = this.getTransactions().get(i);
			value = transaction.getValue();
			if (transaction.getType().equals(TransactionType.EXPENSE)) {
				value *= -1;
			}
			cells = new String[]{String.valueOf(transaction.getID()), transaction.getName(), this.print.formatMoney(value), transaction.getDateStr(), this.print.capitalise(transaction.getCategory()), this.print.capitalise(transaction.getPaymentMethod()), transaction.getRemarks(), this.print.capitalise(transaction.getStatus().toString())};
			this.print.row(cells, cellWidths);
		}
		stats = this.account.cal(this.getTransactions());
		cellWidths = new int[]{6, 13, 79};
		this.print.separator('-');
		cells = new String[]{"", "IN", this.print.formatMoney(stats[0])};
		this.print.row(cells, cellWidths);
		cells = new String[]{"", "OUT", this.print.formatMoney(stats[1])};
		this.print.row(cells, cellWidths);
		this.print.separator('-');
		cells = new String[]{"", "NET", this.print.formatMoney(stats[2])};
		this.print.row(cells, cellWidths);
		this.print.separator('-');
		cells = new String[]{"", "CURRENCY", account.getCurrency()};
		this.print.row(cells, cellWidths);
		this.print.blankLine();
		this.title = new String();
	}

	// Print budget planner
	private void printBudgetPlanner() {
		double value;
		double[] stats;
		String[] cells;
		int[] cellWidths;
		BudgetPlannerItem transaction;
		cellWidths = new int[]{6, 13, 12, 13, 15, 15, 13, 11};
		cells = new String[]{"ID", "NAME", "VALUE", "DATE", "CATEGORY", "METHOD", "REMARKS", "PRIORITY"};
		this.print.blankLine();
		this.print.row(cells, cellWidths);
		this.print.separator('-');
		for (int i=0; i<this.getTransactions().size(); i++) {
			transaction = (BudgetPlannerItem) this.getTransactions().get(i);
			value = transaction.getValue() * -1;
			cells = new String[]{String.valueOf(transaction.getID()), transaction.getName(), this.print.formatMoney(value), transaction.getDateStr(), this.print.capitalise(transaction.getCategory()), this.print.capitalise(transaction.getPaymentMethod()), transaction.getRemarks(), transaction.getPriority() + ""};
			this.print.row(cells, cellWidths);
		}
		stats = this.account.cal(this.getTransactions());
		cellWidths = new int[]{6, 13, 79};
		this.print.separator('-');
		cells = new String[]{"", "BUDGET", this.print.formatMoney(this.account.getBudgetPlanner().getBudget())};
		this.print.row(cells, cellWidths);
		cells = new String[]{"", "BUDGET SPENT", this.print.formatMoney(stats[1])};
		this.print.row(cells, cellWidths);
		if (this.account.getBudgetPlanner().getBudget() != 0) {
			double budgetLeft = this.account.getBudgetPlanner().getBudget() + stats[1];
			this.print.separator('-');
			if (budgetLeft <= 0) {
				budgetLeft = 0;
			}
			cells = new String[]{"", "BUDGET LEFT", this.print.formatMoney(budgetLeft)};
			this.print.row(cells, cellWidths);
		}
		this.print.separator('-');
		cells = new String[]{"", "CURRENCY", account.getCurrency()};
		this.print.row(cells, cellWidths);
		this.print.blankLine();
		this.title = new String();
	}

	//@author A0084731E
	// Print report
	private void printReport(String date, TreeMap<Integer, double[]> data) {
		int[] cellWidths;
		String[] cells;
		double[] stats;
		this.print.command("Report");
		this.print.blankLine();
		this.print.centreAlign(this.print.spaceOut(date));
		this.print.separator('=');
		this.print.blankLine();
		cellWidths = new int[]{26,12,12,12,12,24};
		if(data.size()==1) { //day
			transactions = account.filterByDate(date);
			this.printTransactions();
			return;
		} else if (data.size()==13) { //year
			cells = new String[]{"", "MONTH", "IN", "OUT", "NET", ""};
			this.print.row(cells, cellWidths);
			this.print.separator('-');
			for(int i=1;i<=12;i++) {
				stats = data.get(i);
				cells = new String[]{" ", String.valueOf(i), this.print.formatMoney(stats[0]), this.print.formatMoney(stats[1]), this.print.formatMoney(stats[2]), " " };
				this.print.row(cells, cellWidths);
			}
			this.print.separator('-');
			stats = data.get(0);
			cells = new String[]{" ", "TOTAL", this.print.formatMoney(stats[0]), this.print.formatMoney(stats[1]), this.print.formatMoney(stats[2]), " "};
			this.print.row(cells, cellWidths);
		} else { //month
			cells = new String[]{"", "DAY", "IN", "OUT", "NET", ""};
			this.print.row(cells, cellWidths);
			this.print.separator('-');
			for(int i=1;i<data.size();i++) {
				stats = data.get(i);
				cells = new String[]{" ", String.valueOf(i), this.print.formatMoney(stats[0]), this.print.formatMoney(stats[1]), this.print.formatMoney(stats[2]), " " };
				this.print.row(cells, cellWidths);
			}
			this.print.separator('-');
			stats = data.get(0);
			cells = new String[]{" ", "TOTAL", this.print.formatMoney(stats[0]), this.print.formatMoney(stats[1]), this.print.formatMoney(stats[2]), " "  };
			this.print.row(cells, cellWidths);
		}

		this.print.separator('-');
		cells = new String[]{" ", "CURRENCY", account.getCurrency(), " ", " ", " "};
		this.print.row(cells, cellWidths);
		this.print.blankLine();

	}

	//@author A0085897A
	// Print categories
	public void printCategories(List<String> list) {
		this.print.blankLine();
		for (int i=0; i<list.size(); i++) {
			String category = list.get(i);
			if (category.equals(Account.DEFAULT_CATEGORY) && account.categorySize(Account.DEFAULT_CATEGORY) == 0) {
				continue;
			}
			this.print.centreAlign(this.print.capitalise(category) + " (" + account.categorySize(category) + ")");
		}
		this.print.blankLine();
	}

	// Print payment method
	public void printPaymentMethod(List<String> list) {
		this.print.blankLine();
		for (int i=0; i<list.size(); i++) {
			String paymentMethod = list.get(i);
			if (paymentMethod.equals(Account.DEFAULT_PAYMENT_METHOD) && account.paymentMethodSize(Account.DEFAULT_PAYMENT_METHOD) == 0) {
				continue;
			}
			this.print.centreAlign(this.print.capitalise(paymentMethod) + " (" + account.paymentMethodSize(paymentMethod) + ")");
		}
		this.print.blankLine();
	}

	// Print splash screen
	public void printSplash() {
		this.print.command("Hello!");
		this.print.file(SPLASH_FILE, true);
		this.print.separator();
		this.print.feedback("Type \"help\" for the list of available commands");
	}

	// Print help menu
	public void printHelp() {
		this.print.command("Help");
		this.print.file(HELP_FILE, false);
	}

	// Print alert
	public void alert() {
		double[] stats = this.account.cal(this.account.getTransactions().getTransactions());
		if (!this.alert && stats[2] < 0) {
			this.print.separator();
			this.print.feedback("ALERT: YOUR ACCOUNT BALANCE (" + this.print.formatMoney(stats[2]) + ") IS NEGATIVE");
			this.alert = true;
		} else if (this.alert && stats[2] >= 0) {
			this.alert = false;
		}
	}

	// Import from file
	private static List<Transaction> importFile(String filename) throws Exception {
		FileImport fileImport = new FileImport(filename);
		return fileImport.getTransactions();
	}

	// Export to file
	@SuppressWarnings("unused")
	private static void exportFile(Transactions transactions, String filename) throws WriteException, IOException {
		int index = filename.lastIndexOf("/");
		if (index != -1) {
			File file = new File(filename.substring(0, index));
			if (!file.isDirectory()) {
				file.mkdirs();
			}
		}
		FileExport fileExport = new FileExport(transactions, filename);
	}

	//@author A0083338B
	// DRIVER PROGRAM
	public static void main(String[] args) {
		Driver driver = new Driver();
		driver.printSplash();
		while (true) {
			driver.run();
		}
	}


}
